Description
Add a glint command/keybind to export the current document as a clean, printable PDF (or print-ready HTML) in the AppKit house style โ matching what _shared-app-kit/markdown-doc-kit already produces for the browser.
Reuse that kit as the source of truth rather than reinventing styling: it has doc.css (tokens, the seven themes โ flexoki/-dark, uchu/-dark, humdrum/-dark, eink โ document typography, and the @media print rules), embedded fonts (Awke, Untitled Sans, Name Mono), and pagination conventions (opening '# Title' becomes a centered cover page; every '## Section' starts a new page; '{.page-break}' forces a break; US Letter, 1in margins; print collapses to black-on-white).
Approach options to decide during design:
- Generate a self-contained HTML (doc.css + fonts + rendered markdown baked in, like the kit's 'Save HTML') and either (a) open it in the browser so the user hits Print โ Save as PDF, or (b) shell out to a headless converter (Chromium --headless --print-to-pdf, wkhtmltopdf, or weasyprint/pandoc+Prince) to write a .pdf directly.
- Keep glint dependency-light: prefer 'emit HTML + open in browser' as the zero-extra-binary path; treat direct-to-PDF (needs an external tool) as optional/detected.
- Map glint's active theme to the kit theme where they line up (flexoki light/dark already shared).
Out of scope: reimplementing the kit's interactive features (checkbox toggle, drag reorder) in glint.
Acceptance Criteria
- #1 A glint command/keybind exports the current buffer to a printable artifact (PDF or print-ready self-contained HTML) in the markdown-doc-kit house style
- #2 Output reuses the kit's doc.css + embedded fonts and its print conventions (cover page from '# Title', page-per '## Section', US Letter, black-on-white print)
- #3 The chosen path is documented (browser print-to-PDF vs headless converter) and degrades gracefully when an optional external converter is absent
- #4 README/help document the export command
- #5 Fonts are user-configurable (config keys for display/body/mono), not hard-wired to the kit's bundled Awke/Untitled Sans/Name Mono โ glint is distributed to other people who don't have those licensed fonts
- #6 Export is portable off this machine: ships sane open/system-font fallbacks by default and works with no personal assets present; any bundled font must be redistributable, otherwise reference by name with fallbacks
- #7 The needed kit assets (doc.css + print rules, HTML skeleton, any redistributable fonts) are vendored INTO this repo and embedded in the binary via go:embed โ nothing is read from the external _shared-app-kit path at runtime, so a 'brew install glint' on another machine has everything it needs
Implementation Plan
- Vendor kit assets into internal/export/assets/: doc.css (neutralize the 3 --font-* :root tokens to open/system fallbacks, strip nothing else โ no @font-face present) + sync.sh that re-copies from _shared-app-kit and re-neutralizes. go:embed them.
- config: add font_display/font_body/font_mono keys (Config fields + Default + loadFromFile overlay), defaults = Georgia serif / system-ui sans / ui-monospace.
- internal/export package (TDD): Document(md, Options)->html pure fn โ goldmark GFM render, strip YAML frontmatter, postprocess (cover-wrap leading h1+subtitle p, task-list-item class, {.page-break} heading suffix->class), inject :root font-token override from Options, wrap in article.doc + embedded doc.css. MapTheme(glintTheme)->kit data-theme. ToFile writes .html next to source. OpenInBrowser (darwin open / linux xdg-open / win start).
- Wire Ctrl+E in app.handleKey -> export current buffer, write html next to file, open browser, set status. Graceful: if no path (unnamed) save-as first or export to temp.
- README + help overlay: document Ctrl+E export, the browser Print->Save-as-PDF path, and the 3 font config keys.
- Tests: Document output contains doc.css, cover div, page-break class, configured fonts, mapped theme; frontmatter stripped.
Implementation Notes
Portability constraint (user): glint is installed by people who are NOT on this machine and do NOT have the kit's bundled fonts (Awke, Untitled Sans, Name Mono โ personal/licensed). So the export must NOT hard-embed those. Fonts must be user-configurable (display/body/mono config keys, reusing the doc.css --font-* tokens) with safe open/system fallbacks (e.g. Georgia / system-ui / ui-monospace) so output looks fine with zero personal assets. Only redistributable fonts may be embedded by default.
Packaging: glint must be self-contained. Copy/build the required kit pieces (doc.css + the @media print rules, the render HTML skeleton, only redistributable fonts) into the glint repo (e.g. internal/export/assets/) and embed them with go:embed so they ship inside the binary. Do NOT depend on /Users/kortum/Developer/Home/_shared-app-kit/markdown-doc-kit at runtime โ that path doesn't exist on users' machines. Decide a vendor/sync story (a script that re-copies from the kit on update, or a one-time fork) so the embedded copy can be refreshed when the kit's house style changes; strip the licensed Awke/Untitled Sans/Name Mono @font-face blocks during that copy, leaving the --font-* tokens + fallbacks (ties to the configurable-fonts AC).
Implemented: internal/export package (TDD, 17 tests). Document() renders md via goldmark GFM, strips YAML frontmatter, postprocesses (cover-wrap leading h1+subtitle, task-list-item class, {.page-break} heading suffix->class), embeds vendored doc.css (go:embed) + injects configured --font-* tokens. MapTheme glint->kit. Write()/OutputPath()/Title()/OpenInBrowser() for file+browser. Ctrl+E wired in app.exportPDF (writes .html next to file, opens browser, graceful path-print fallback if open fails). config: font_display/body/mono keys w/ portable defaults (Georgia/system-ui/ui-monospace); wizard preserves them. assets/sync.sh re-vendors doc.css from kit + strips licensed faces (Awke/Untitled Sans/Name Mono). README Export section + help overlay updated. Fixed bug found in real output: heading {.class} regex used (?s) dotall and leaked page-break across to an earlier heading w/ mismatched close tag; removed dotall.
Follow-up fix: WebKit (Safari/Orion) blocks locally-installed user fonts (~/Library/Fonts) from web content, so custom configured fonts rendered as default serif. Added font embedding (internal/export/fonts.go): scans user font dirs, parses sfnt name tables (golang.org/x/image/font/sfnt), inlines matching faces as base64 @font-face (bounded to regular+bold x roman+italic, ~3MB). Best-effort + portable: unfound fonts fall back to name reference; binary still bundles nothing licensed. Also quote multi-word family names in the CSS override (WebKit var() quirk). Default fonts (Georgia/system-ui/ui-monospace) are WebKit-allowed so default exports were unaffected.